import { Rect as GRect, Text as GText, TextStyleProps } from "@antv/g";

import {
  BaseEdge,
  ExtensionCategory,
  Graph,
  GraphData,
  IKeyboardEvent,
  IPointerEvent,
  NodeData,
  Rect,
  register,
  treeToGraphData,
  LabelStyleProps,
} from "@antv/g6";
import { useCallback, useEffect, useRef, useState } from "react";
import { treeData } from "./qfg4data";
import Input from "antd/es/input/Input";
import { Button, Space } from "antd";

interface INodeData extends NodeData {
  label?: string;
  data?: { label: string; value?: number; itemize: { label?: string; value?: number; unit?: string }[] };
}

class CardNode extends Rect {
  get data() {
    const data = this.context.model.getNodeLikeDatum(this.id) as INodeData;
    return data;
  }
  get topAreaHeight() {
    return 27;
  }

  getContentSize(attributes) {
    const [width, height] = this.getSize(attributes);
    return {
      x: -width / 2 + 1,
      y: -height / 2 + 1,
      width: width - 2,
      height: height - 2,
    };
  }
  getStateStyle(attributes) {
    return {
      opacity: attributes.opacity,
    };
  }

  getLabelStyle(attributes) {
    const { x, y } = this.getContentSize(attributes);
    const data = this.data;

    const prop = {
      text: data.label,
      x: 0,
      y: y + this.topAreaHeight - 14,
      fontSize: 14,
      textAlign: "center",
      fill: "#fff",
      // backgroundFill: "red",
      zIndex: 1,

      ...this.getStateStyle(attributes),
    } as LabelStyleProps;
    return prop;
  }

  // 背景
  getKeyStyle(attributes) {
    return {
      ...super.getKeyStyle(attributes),
      ...this.getStateStyle(attributes),
    };
  }
  drawKeyBotShape(attributes, container) {
    const { x, y, width, height } = this.getContentSize(attributes);
    const attr = {
      x: x,
      y: y,
      width: width,
      height: height,
      fill: "#fff",

      ...this.getStateStyle(attributes),
    };
    this.upsert("keyBot", GRect, attr, container);

    const topAttr = {
      x: x,
      y: y,
      width: width,
      height: this.topAreaHeight,
      fill: attributes.fill,

      ...this.getStateStyle(attributes),
    };
    this.upsert("keyTop", GRect, topAttr, container);
  }

  drawItemShape(attributes, container, bbox, v, i) {
    this.upsert("itemizeBg_" + i, GRect, { ...bbox, fill: i % 2 === 1 ? attributes.fill : undefined, fillOpacity: 0.2 }, container);

    const cx = bbox.x + bbox.width / 2;
    const ty = bbox.y + 10;
    const textShape = this.upsert(
      "itemizeTitle_" + i,
      GText,
      {
        x: cx,
        y: ty,
        textAlign: "center",
        textBaseline: "top",
        wordWrap: true,
        wordWrapWidth: bbox.width - 4,
        text: v.label,
        fill: "#2E3033",
        fontSize: 14,

        ...this.getStateStyle(attributes),
      },
      container
    );
    // const textShape = this.shapeMap["itemizeTitle_" + i];
    const ith = textShape.getBoundingClientRect().height;

    const valShape = this.upsert(
      "itemizeValue_" + i,
      GText,
      {
        x: cx,
        y: ty + ith,
        textAlign: "center",
        textBaseline: "top",
        text: `${v.value} ${v.unit || ""}`,
        fill: "#2E3033",
        fontSize: 16,

        ...this.getStateStyle(attributes),
      },
      container
    );
  }
  drawItemizeShape(attributes, container) {
    const { x, y, width, height } = this.getContentSize(attributes);

    const d = this.data.data;
    let itemize = d.itemize || [];
    if (d.value) {
      if (itemize.length) {
        itemize = [{ label: "总数", value: d.value }, ...itemize];
      } else {
        itemize = [{ label: "", value: d.value }];
      }
    }
    const len = itemize.length;
    itemize.map((v, i) => {
      const attr = {
        x: (width / len) * i + x,
        y: this.topAreaHeight + y,
        width: width / len,
        height: height - (this.topAreaHeight || 0),
        zIndex: 2,
      };
      this.drawItemShape(attributes, container, attr, v, i);
    });
  }

  render(attributes = this.parsedAttributes, container) {
    super.render(attributes, container);

    this.drawKeyBotShape(attributes, container);
    this.drawItemizeShape(attributes, container);
  }
}
register(ExtensionCategory.NODE, "card-node", CardNode);

class PolylineEdge extends BaseEdge {
  getKeyPath(attributes) {
    const [sourcePoint, targetPoint] = this.getEndpoints(attributes);

    return [
      ["M", sourcePoint[0], sourcePoint[1]],
      ["L", sourcePoint[0] + 40, sourcePoint[1]],
      ["L", targetPoint[0] - 40, targetPoint[1]],
      ["L", targetPoint[0], targetPoint[1]],
    ] as ReturnType<InstanceType<typeof BaseEdge>["getKeyPath"]>;
  }
}

register(ExtensionCategory.EDGE, "custom-polyline", PolylineEdge);

const getNodeSide = (graph, datum) => {
  const parentData = graph.getParentData(datum.id, "tree");
  if (!parentData) return "center";
  return datum.style.x > parentData.style.x ? "right" : "left";
};

const ChartDemo1 = () => {
  const ref = useRef<HTMLDivElement & { graph?: Graph }>(null);

  const getData = useCallback(() => {
    // return fetch("https://assets.antv.antgroup.com/g6/combo.json")
    // .then((res) => res.json())
    // .then((data) => {

    const graph = ref.current.graph;

    let data = { id: "root", label: "跟", children: treeData };
    let gd = {
      nodes: [
        // { id: "root", label: "根节点" } as any
        // ...new Array(10).fill(1).map((v, i) => ({ id: "m" + i, label: "层级1：" + i })),
      ],
      edges: [
        // ...new Array(9).fill(1).map((v, i) => ({ source: `m${i}`, target: `m${i + 1}`, type: "polyline" })),

        { source: "t11", target: "t3" },
        { source: "t12", target: "t3" },
        { source: "t13", target: "t3" },
        { source: "t14", target: "t3" },

        // ===
        { source: "t14", target: "t3", style: { visibility: "hidden" } },
        { source: "t14", target: "ta3", style: { visibility: "hidden" } },

        { source: "d3511", target: "t4", style: { visibility: "hidden" } },
        { source: "d3511", target: "t5", style: { visibility: "hidden" } },

        { source: "t43", target: "t6", style: { visibility: "hidden" } },
        { source: "t43", target: "t7", style: { visibility: "hidden" } },
        { source: "t43", target: "t8", style: { visibility: "hidden" } },
      ],
      combos: [],
    } as GraphData;
    treeData.map((v) => {
      const d = treeToGraphData(v, {
        getNodeData: (datum, depth) => {
          if (!datum.style) datum.style = {};
          // datum.style.collapsed = depth >= 2;

          const { children = [], ...restDatum } = datum;
          const nd = {
            ...restDatum,
            combo: restDatum.shape,
            children: children.map((child) => child.id),
          } as NodeData;

          if (gd.combos.find((c) => c.id === datum.shape) === undefined && datum.shape) {
            gd.combos.push({ id: datum.shape, type: "rect" });
          }

          if (!datum.children) delete nd.children;
          return nd;
        },
      });
      gd.nodes.push(...d.nodes);
      gd.edges.push(...d.edges);
      // if (gd.combos.find((c) => c.id === v.shape) === undefined) {
      //   gd.combos.push({ id: v.shape, type: "rect" });
      // }

      // if (v.id !== "t3") {
      //   gd.edges.push({ source: "root", target: v.id });
      // }
    });

    graph.setData(gd);
    graph.render();
    // });
  }, []);

  const reloadFn = useCallback(() => {
    const graph = new Graph({
      container: ref.current,
      background: "#F2F7FA",
      autoFit: "center",
      node: {
        type: "card-node",
        style: {
          size: [274, 90],

          // iconText: (d: any) => d.id,
          // iconFontSize: 12,
          // labelText: (d: any) => d.label,
          // labelBackground: true,
          ports: [{ placement: "right" }, { placement: "left" }],
        },
        state: {
          light: {
            opacity: 1,
          },
          dark: {
            opacity: 0.2,
          },
        },
        palette: {
          type: "group",
          field: (d) => d.shape as string,
          // field: "shape",
        },
        animation: {
          enter: false,
        },
      },
      edge: {
        type: (d) => d.type || "custom-polyline",
        style: {
          endArrow: true,
        },
        state: {
          light: {
            opacity: 1,
          },
          dark: {
            opacity: 0.2,
          },
        },
        animation: {
          enter: false,
        },
      },
      combo: {
        style: {
          labelText: (d) => d.id,
          labelPlacement: "top",
        },
      },
      layout: {
        type: "antv-dagre",
        ranksep: 40,
        nodesep: -40,
        rankdir: "LR",
      },
      plugins: [
        { key: "fullscreen", type: "fullscreen" },
        {
          key: "toolbar",
          type: "toolbar",

          getItems: () => {
            return [
              // { id: "zoom-in", value: "zoom-in" },
              // { id: "zoom-out", value: "zoom-out" },
              // { id: "auto-fit", value: "auto-fit" },
              // { id: "reset", value: "reset" },
              // { id: "fullscreen", value: "request-fullscreen" },
            ];
          },
        },
      ],

      behaviors: [
        {
          key: "lasso-select",
          type: "lasso-select",
          mode: "diff",
          trigger: "shift",
          style: {
            fill: "#00f",
            fillOpacity: 0.1,
            stroke: "#0ff",
            lineWidth: 2,
          },
        },
        {
          key: "drag-canvas",
          type: "drag-canvas",
          enable: (ev: IPointerEvent | IKeyboardEvent) => {
            if (ev.shiftKey || (ev as any).targetType !== "canvas") {
              return false;
            }
            return true;
          },
        },
        // "collapse-expand",
        "drag-element",
        "zoom-canvas",
      ],
      animation: false,
    });

    ref.current.graph = graph;
    graph.render();

    getData();
  }, []);

  useEffect(() => {
    reloadFn();
  }, [reloadFn]);

  useEffect(() => {
    const toolbarItems = [
      { id: "zoom-in", value: "zoom-in" },
      { id: "zoom-out", value: "zoom-out" },
      { id: "auto-fit", value: "auto-fit" },
      { id: "reset", value: "reset" },
      { id: "request-fullscreen", value: "fullscreen" },
    ]
      .map(
        (item) => `
        <div class="g6-toolbar-item" value="${item.value}">
          <svg aria-hidden="true" focusable="false">
            <use xlink:href="#${item.id}"></use>
          </svg>
        </div>`
      )
      .join("");
    ref.current.parentElement.getElementsByClassName("boolbar-custom")[0].innerHTML = toolbarItems;
  }, []);

  const [search, setSearch] = useState<string>();
  const toolbarClick = useCallback(
    (e) => {
      const graph = ref.current.graph;
      if (typeof e === "string") {
        const v = e || "";
        switch (v) {
          case "action-search": {
            graph.getNodeData().map((n: INodeData) => {
              // graph.getRelatedEdgesData(n.id)
              if ((search && n.label.indexOf(search) > -1) || !search) {
                graph.setElementState(n.id, "light");
              } else {
                graph.setElementState(n.id, "dark");
              }
              return undefined;
            });
            graph.getEdgeData().map((n) => {
              if (search) {
                graph.setElementState(n.id, "dark");
              } else {
                graph.setElementState(n.id, "light");
              }
              return undefined;
            });
          }
        }
        return;
      }

      if (e.target instanceof Element) {
        if (e.target.className.includes("g6-toolbar-item")) {
          const item = e.target.getAttribute("value") || "v";
          switch (item) {
            case "zoom-in": {
              graph.zoomBy(1.25);
              break;
            }
            case "zoom-out": {
              graph.zoomBy(0.8);
              break;
            }
            case "auto-fit": {
              graph.fitCenter();
              break;
            }
            case "reset": {
              // graph.fitView();
              ref.current.innerHTML = "";
              reloadFn();
              break;
            }
            case "fullscreen": {
              const fullscreen = graph.getPluginInstance("fullscreen");
              (fullscreen as any).request();
            }
          }
        }
      }
    },
    [search, reloadFn]
  );

  return (
    <>
      <h2>官方：脑图树示例 - 6 部门预算</h2>
      <div style={{ position: "relative" }}>
        <div
          style={{
            position: "absolute",
            display: "flex",
            top: "8px",
            right: "8px",
            bottom: "unset",
            left: "unset",
            flexDirection: "row",
            zIndex: 100,
            userSelect: "none",
          }}
        >
          <Space.Compact style={{ marginRight: 16 }} size="small">
            <Input
              value={search}
              onChange={(ev) => {
                setSearch(ev.target.value);
              }}
              allowClear
              onClear={() => {
                setSearch("");
                toolbarClick("action-search");
              }}
              style={{ width: 180 }}
              placeholder="请输入指标内容"
            />
            <Button type="primary" onClick={() => toolbarClick("action-search")}>
              搜索
            </Button>
          </Space.Compact>
          <div className="g6-toolbar boolbar-custom" onClick={toolbarClick} style={{ position: "static", background: "#fff" }}></div>
        </div>
        <div ref={ref} style={{ width: "100%", minHeight: 600 }}></div>
      </div>
    </>
  );
};

export default ChartDemo1;
